package health_system.health_system.websocket.Server;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import health_system.health_system.websocket.Model.CommunicationUtils;
import health_system.health_system.websocket.NettyMediaCommunicationMessage;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.*;
import io.netty.handler.codec.http.websocketx.*;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.util.AttributeKey;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component;

import javax.net.ssl.KeyManagerFactory;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.text.SimpleDateFormat;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * 基于Netty实现的统一通信WebSocket服务器
 * 处理用户间的视频通话、语音通话和屏幕共享的信令
 */
@Component
public class NettyMediaCommunicationServer {
    private static final Logger logger = Logger.getLogger(NettyMediaCommunicationServer.class.getName());
    private static final Map<String, Channel> channelMap = new ConcurrentHashMap<>();
    private static final ObjectMapper mapper = new ObjectMapper();
    private static final String SYSTEM_MESSAGE = "系统消息";
    private static final String USER_OFFLINE_MESSAGE = "Sorry，呼叫的用户不在线！";
    private static final String HANGUP_MESSAGE = "对方挂断！";
    private static final AttributeKey<String> USERNAME_ATTR = AttributeKey.valueOf("username");

    private EventLoopGroup bossGroup;
    private EventLoopGroup workerGroup;
    private Channel serverChannel;

    @Value("${netty.websocket.port:8085}")
    private int port;

    @Value("${netty.ssl.enabled:false}")
    private boolean sslEnabled;

    @Value("${netty.ssl.key-store:}")
    private String keyStorePath;

    @Value("${netty.ssl.key-store-type:PKCS12}")
    private String keyStoreType;

    @Value("${netty.ssl.key-store-password:}")
    private String keyStorePassword;

    @Value("${netty.ssl.key-alias:}")
    private String keyAlias;

    static {
        mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
    }

    /**
     * 启动Netty WebSocket服务器
     */
    public void start() throws Exception {
        bossGroup = new NioEventLoopGroup(1);
        workerGroup = new NioEventLoopGroup();

        try {
            // 创建SSL上下文（如果启用）
            final SslContext sslContext = createSslContext();

            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline pipeline = ch.pipeline();

                            // 添加SSL处理器（如果启用）
                            if (sslContext != null) {
                                pipeline.addLast(sslContext.newHandler(ch.alloc()));
                            }

                            // HTTP请求解码器
                            pipeline.addLast(new HttpServerCodec());
                            // 聚合HTTP消息
                            pipeline.addLast(new HttpObjectAggregator(65536));
                            // 处理大数据流
                            pipeline.addLast(new ChunkedWriteHandler());
                            // WebSocket处理器
                            pipeline.addLast(new WebSocketServerHandler());
                        }
                    })
                    .option(ChannelOption.SO_BACKLOG, 128)
                    .childOption(ChannelOption.SO_KEEPALIVE, true);

            Channel channel = bootstrap.bind(port).sync().channel();
            this.serverChannel = channel;

            logger.info("Netty WebSocket 服务器启动成功，端口: " + port +
                       (sslEnabled ? " (SSL启用)" : ""));

            // 等待服务器关闭
            channel.closeFuture().sync();
        } finally {
            shutdown();
        }
    }

    /**
     * 关闭Netty WebSocket服务器
     */
    public void shutdown() {
        if (bossGroup != null) {
            bossGroup.shutdownGracefully();
        }
        if (workerGroup != null) {
            workerGroup.shutdownGracefully();
        }
        logger.info("Netty WebSocket 服务器已关闭");
    }

    /**
     * 创建SSL上下文
     * @return SSL上下文，如果未启用SSL则返回null
     */
    private SslContext createSslContext() {
        if (!sslEnabled) {
            return null;
        }

        try {
            // 检查keystore配置
            if (keyStorePath == null || keyStorePath.isEmpty() ||
                keyStorePassword == null || keyStorePassword.isEmpty()) {
                logger.warning("SSL已启用，但keystore路径或密码未配置");
                return null;
            }

            // 加载keystore文件
            String keystoreFilePath = keyStorePath;
            if (keystoreFilePath.startsWith("classpath:")) {
                keystoreFilePath = keystoreFilePath.substring("classpath:".length());
            }

            ClassPathResource resource = new ClassPathResource(keystoreFilePath);
            if (!resource.exists()) {
                logger.warning("Keystore文件不存在: " + keyStorePath);
                return null;
            }

            // 创建KeyStore实例
            KeyStore keyStore = KeyStore.getInstance(keyStoreType);
            try (InputStream keyStoreStream = resource.getInputStream()) {
                keyStore.load(keyStoreStream, keyStorePassword.toCharArray());
            }

            // 创建KeyManagerFactory
            KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
            kmf.init(keyStore, keyStorePassword.toCharArray());

            // 构建SslContext
            return SslContextBuilder.forServer(kmf).build();

        } catch (KeyStoreException | NoSuchAlgorithmException | CertificateException |
                 IOException | UnrecoverableKeyException e) {
            logger.log(Level.SEVERE, "创建SSL上下文失败: " + e.getMessage(), e);
            return null;
        }
    }

    /**
     * 从查询字符串中提取用户名
     * @param queryString 查询字符串
     * @return 用户名，如果未找到则返回null
     */
    private static String extractUsernameFromQueryString(String queryString) {
        if (queryString == null || !queryString.contains("username=")) {
            return null;
        }
        String[] parts = queryString.split("username=");
        if (parts.length < 2) {
            return null;
        }
        String username = parts[1];
        int ampIndex = username.indexOf('&');// 处理可能存在的其他参数
        if (ampIndex != -1) {
            username = username.substring(0, ampIndex);
        }
        return username;
    }

    /**
     * 处理WebSocket消息
     * @param channel 客户端通道
     * @param message 消息内容
     */
    private void handleWebSocketMessage(Channel channel, String message) {
        if (message == null || message.isEmpty()) {
            logger.warning("接收到空消息");
            return;
        }

        logger.fine("接收到消息: " + message);

        try {
            NettyMediaCommunicationMessage commMessage =
                mapper.readValue(message, NettyMediaCommunicationMessage.class);

            String type = commMessage.getType();
            String toUser = commMessage.getToUser();
            String fromUser = commMessage.getFromUser();
            String mode = commMessage.getMode(); // 通信模式：video, audio, screen

            if (type == null || toUser == null) {
                logger.warning("消息格式无效: 缺少必需字段");
                return;
            }

            // 如果未指定模式，默认为视频通话
            if (mode == null) {
                mode = CommunicationUtils.getDefaultMode();
                commMessage.setMode(mode);
            }

            // 记录通信事件
            CommunicationUtils.logCommunicationEvent(
                fromUser,
                mode,
                type,
                "To: " + toUser + (commMessage.getMsg() != null ? ", Msg: " + commMessage.getMsg() : "")
            );

            Channel toUserChannel = channelMap.get(toUser);
            NettyMediaCommunicationMessage responseMessage = new NettyMediaCommunicationMessage();
            responseMessage.setType(type);
            responseMessage.setMode(mode);

            if (toUserChannel == null || !toUserChannel.isActive()) {
                responseMessage.setType(CommunicationUtils.MessageType.CALL_BACK.getValue());
                responseMessage.setFromUser(SYSTEM_MESSAGE);
                responseMessage.setMsg(USER_OFFLINE_MESSAGE);
                sendMessage(channel, responseMessage);
                return;
            }

            // 处理不同类型的消息
            CommunicationUtils.MessageType messageType = CommunicationUtils.MessageType.fromString(type);
            if (messageType == null) {
                logger.warning("未知消息类型: " + type);
                return;
            }

            switch (messageType) {
                case HANGUP:
                    responseMessage.setFromUser(fromUser);
                    responseMessage.setMsg(HANGUP_MESSAGE);
                    break;
                case CALL_START:
                    responseMessage.setFromUser(fromUser);
                    responseMessage.setMsg("1");
                    break;
                case CALL_BACK:
                    responseMessage.setFromUser(toUser);
                    responseMessage.setMsg(commMessage.getMsg());
                    break;
                case OFFER:
                case ANSWER:
                    responseMessage.setFromUser(toUser);
                    responseMessage.setSdp(commMessage.getSdp());
                    break;
                case ICE:
                    responseMessage.setFromUser(toUser);
                    responseMessage.setSdpMid(commMessage.getSdpMid());
                    responseMessage.setSdpMLineIndex(commMessage.getSdpMLineIndex());
                    responseMessage.setCandidate(commMessage.getCandidate());
                    break;
                default:
                    logger.warning("未处理的消息类型: " + type);
                    return;
            }

            // 确保响应消息包含通信模式
            responseMessage.setMode(mode);

            // 发送响应消息
            sendMessage(toUserChannel, responseMessage);

        } catch (Exception e) {
            logger.log(Level.SEVERE, "处理消息时出错: " + e.getMessage(), e);
        }
    }

    /**
     * 发送消息到指定通道
     * @param channel 目标通道
     * @param message 消息对象
     */
    private void sendMessage(Channel channel, NettyMediaCommunicationMessage message) {
        try {
            String jsonMessage = mapper.writeValueAsString(message);
            sendMessage(channel, jsonMessage);
        } catch (JsonProcessingException e) {
            logger.log(Level.SEVERE, "序列化消息失败", e);
        }
    }

    /**
     * 发送文本消息到指定通道
     * @param channel 目标通道
     * @param message 消息文本
     */
    private void sendMessage(Channel channel, String message) {
        if (channel == null || !channel.isActive()) {
            logger.warning("尝试向无效通道发送消息");
            return;
        }
        try {
            logger.fine("发送消息: " + message);
            TextWebSocketFrame frame = new TextWebSocketFrame(message);
            channel.writeAndFlush(frame);
        } catch (Exception e) {
            logger.log(Level.SEVERE, "发送消息时出错", e);
        }
    }

    /**
     * WebSocket处理器
     */
    private class WebSocketServerHandler extends SimpleChannelInboundHandler<Object> {
        private WebSocketServerHandshaker handshaker;

        @Override
        protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
            if (msg instanceof FullHttpRequest) {
                handleHttpRequest(ctx, (FullHttpRequest) msg);
            } else if (msg instanceof WebSocketFrame) {
                handleWebSocketFrame(ctx, (WebSocketFrame) msg);
            }
        }

        /**
         * 处理HTTP请求（WebSocket握手）
         */
        private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest req) {
            // 检查是否为WebSocket升级请求
            if (!req.headers().contains(HttpHeaderNames.UPGRADE) ||
                !HttpHeaderValues.WEBSOCKET.contentEqualsIgnoreCase(req.headers().get(HttpHeaderNames.UPGRADE))) {
                // 返回普通HTTP响应
                FullHttpResponse res = new DefaultFullHttpResponse(
                    HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST);
                ctx.channel().writeAndFlush(res);
                return;
            }

            // 获取WebSocket路径
            String uri = req.uri();
            if (!uri.startsWith("/RemoteAddressForVideoChat")) {
                ctx.close();
                return;
            }

            // 从查询字符串中提取用户名
            String queryString = uri.contains("?") ? uri.substring(uri.indexOf("?") + 1) : "";
            String username = extractUsernameFromQueryString(queryString);

            if (username != null) {
                // 将用户名保存到通道属性中
                ctx.channel().attr(USERNAME_ATTR).set(username);
                // 将通道添加到映射中
                channelMap.put(username, ctx.channel());
                logger.info("WebSocket opened for user: " + username);
            } else {
                logger.warning("Invalid username parameter in query string");
                ctx.close();
                return;
            }

            // 完成WebSocket握手
            WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory(
                getWebSocketLocation(req), null, false);
            handshaker = wsFactory.newHandshaker(req);
            if (handshaker == null) {
                WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel());
            } else {
                handshaker.handshake(ctx.channel(), req);
            }
        }

        /**
         * 获取WebSocket地址
         */
        private String getWebSocketLocation(FullHttpRequest req) {
            String location = req.headers().get(HttpHeaderNames.HOST) + req.uri();
            if (sslEnabled) {
                return "wss://" + location;
            } else {
                return "ws://" + location;
            }
        }

        /**
         * 处理WebSocket帧
         */
        private void handleWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame) {
            // 关闭帧
            if (frame instanceof CloseWebSocketFrame) {
                handshaker.close(ctx.channel(), (CloseWebSocketFrame) frame.retain());
                return;
            }

            // Ping帧
            if (frame instanceof PingWebSocketFrame) {
                ctx.channel().writeAndFlush(new PongWebSocketFrame(frame.content().retain()));
                return;
            }

            // 仅支持文本帧
            if (!(frame instanceof TextWebSocketFrame)) {
                throw new UnsupportedOperationException(
                    "不支持的帧类型: " + frame.getClass().getName());
            }

            // 处理文本消息
            String request = ((TextWebSocketFrame) frame).text();
            handleWebSocketMessage(ctx.channel(), request);
        }

        /**
         * 通道激活时调用
         */
        @Override
        public void channelActive(ChannelHandlerContext ctx) throws Exception {
            logger.info("统一通信服务器连接成功: " + ctx.channel().id().asShortText());
            super.channelActive(ctx);
        }

        /**
         * 通道非活动时调用
         */
        @Override
        public void channelInactive(ChannelHandlerContext ctx) throws Exception {
            Channel channel = ctx.channel();
            String username = channel.attr(USERNAME_ATTR).get();

            if (username != null) {
                if (channelMap.containsKey(username)) {
                    logger.info(username + "--->already_offline");
                    channelMap.remove(username);
                }
            }

            super.channelInactive(ctx);
        }

        /**
         * 异常处理
         */
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
            Channel channel = ctx.channel();
            String username = channel.attr(USERNAME_ATTR).get();

            logger.log(Level.SEVERE, "WebSocket error for channel " + channel.id().asShortText() +
                      (username != null ? " (user: " + username + ")" : ""), cause);
            ctx.close();
        }
    }
}